﻿#region File Header
//-----------------------------------------------------------------------------
// Bricks' Bane
//
// Copyright (C) 2010 Julien Villers
// This program is distributed under the terms of the 
// GNU Lesser General Public License (LGPL).
// See Docs/lgpl.txt and Docs/gpl.txt
//-----------------------------------------------------------------------------
#endregion

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SibLib.Containers;
using SibLib.Entities;
using SibLib.Render;
using SibLib.Render.Primitives3D;

namespace BricksBane.Entities
{
    sealed class Paddle : GameplayObject, ICollideable2D
    {
        public enum E_Types
        {
            Standard = 0,
            Laser,
            Magnet,
            Glue,
            Pauser,
        };

        private static readonly Color[] COLORS =
        {
            Color.White,
            Color.Red,
            Color.Blue,
            Color.Green,
            Color.Black,
        };

        private static readonly float EXTENDED_RATIO = 1.5f;

        #region Properties
        public float Width
        {
            get { return (float)GetBoundingBox().Width; }
        }
        /// <summary>
        /// Modifying this after Initialize() has been invoked is useless.
        /// </summary>
        public Vector2 Size
        {
            get;
            set;
        }
        public Color Color
        {
            get;
            private set;
        }
        public E_Types Type
        {
            get { return m_Type; }
            set { m_Type = value; Color = COLORS[(int)value]; }
        }
        public Wall Wall
        {
            get;
            set;
        }
        #endregion

        #region Members
        private Rectangle m_BoundingBox;
        private GeometricPrimitive m_Primitive;
        private Matrix m_World;
        private float m_Scale;
        private float m_Speed;
        private E_Types m_Type;
        private AutoPool<Bullet> m_Bullets;
        private float m_BulletTimer;
        private bool m_IsShieldEnabled;
        private float m_ShieldTimer;
        private int m_ShieldHP;
        #endregion

        #region Setup

        public Paddle() :
            base()
        {
        }

        public override void Initialize()
        {
            base.Initialize();

            m_BoundingBox = new Rectangle((int)(Position.X - Size.X / 2), (int)(Position.Y - Size.Y / 2), (int)Size.X, (int)Size.Y);
            m_Scale = 1.0f;
            ComputeWorldMatrix();
            m_Speed = 0.0f;
            Type = E_Types.Standard;
            m_IsShieldEnabled = false;
            m_ShieldTimer = 0.0f;
            m_ShieldHP = 0;
        }

        protected override void LoadContent()
        {
            base.LoadContent();

            m_Bullets = new AutoPool<Bullet>(32);

            GraphicsDevice device = GameplayScreen.Instance.GraphicsDevice;
            m_Primitive = new RoundedCylinderPrimitive(device, Size.X, Size.Y, 16);
        }

        #endregion

        #region Update

        private static readonly float SPEED_PER_SEC = 600.0f;

        public void Move(float movement)
        {
            m_Speed = movement * SPEED_PER_SEC;
        }

        public override void Update(float timeFactor)
        {
            if (m_Speed != 0.0f)
            {
                float newX = Position.X + (m_Speed * timeFactor);
                Rectangle wallBox = Wall.GetBoundingBox();
                newX = MathHelper.Clamp(newX, wallBox.Left + Width / 2, wallBox.Right - Width / 2.0f);
                float deltaX = newX - m_Position.X;
                m_Position.X = newX;

                if (Type == Paddle.E_Types.Glue)
                {
                    foreach (Ball ball in GameplayScreen.Instance.Balls)
                    {
                        if (ball.IsFrozen)
                        {
                            ball.Position = new Vector2(ball.Position.X + deltaX, m_BoundingBox.Top - (ball.GetBoundingBox().Height / 2.0f));
                        }
                    }
                }

                m_BoundingBox.X = (int)(Position.X - Width / 2);
                ComputeWorldMatrix();
            }

            foreach (Drop drop in GameplayScreen.Instance.DropPool)
            {
                if (!drop.IsActive)
                    continue;

                if (!Rectangle.Intersect(drop.GetBoundingBox(), GetBoundingBox()).IsEmpty)          // TODO: tuning: could decide not to include shield in this case.
                {
                    drop.IsActive = false;
                    GameplayScreen.Instance.OnDropCollected(drop);
                }
            }

            if (m_BulletTimer > 0.0f)
                m_BulletTimer -= timeFactor;

            m_Bullets.ProcessAdds();
            foreach (Bullet bullet in m_Bullets)
            {
                bullet.Update(timeFactor);
                if (!bullet.IsActive)
                    m_Bullets.Remove(bullet);
            }
            m_Bullets.ProcessRemoves();

            if (m_ShieldTimer > 0.0f)
            {
                m_ShieldTimer -= timeFactor;
                if (m_ShieldTimer <= 0.0f)
                    m_IsShieldEnabled = false;
            }
        }

        #endregion

        public Rectangle GetBoundingBox()
        {
            if (m_IsShieldEnabled)
            {
                Rectangle extended = m_BoundingBox;
                extended.Width = (int)(Size.X * m_Scale * EXTENDED_RATIO);
                return extended;
            }
            
            return m_BoundingBox;
        }

        #region Public API

        public void ScaleInc(bool isGrowing)
        {
            float newScale = m_Scale;
            if (isGrowing)
                newScale += 0.2f;
            else
                newScale -= 0.2f;
            ScaleSet(newScale);
        }

        public void ScaleReset()
        {
            ScaleSet(1.0f);
        }

        public bool TryFire()
        {
            if (m_BulletTimer > 0.0f)
                return false;

            m_BulletTimer = 0.6f;       // TODO: tuning

            Rectangle bbox = GetBoundingBox();

            Bullet bulletLeft = m_Bullets.AddNewItem();
            bulletLeft.Position = new Vector2(bbox.Left, bbox.Top);
            bulletLeft.Wall = Wall;
            bulletLeft.Initialize();
            bulletLeft.IsActive = true;

            Bullet bulletRight = m_Bullets.AddNewItem();
            bulletRight.Position = new Vector2(bbox.Right, bbox.Top);
            bulletRight.Wall = Wall;
            bulletRight.Initialize();
            bulletRight.IsActive = true;

            return true;
        }

        public void ActivateShieldHit()
        {
            m_IsShieldEnabled = true;
            if (m_ShieldHP <= 0)
                m_ShieldHP = 3;     // TODO: tuning
            else
                m_ShieldHP += 3;
            m_ShieldTimer = 0.0f;
        }

        public void ActivateShieldTime()
        {
            m_IsShieldEnabled = true;
            m_ShieldHP = -1;
            if (m_ShieldTimer <= 0.0f)
                m_ShieldTimer = 10.0f;  // TODO: tuning
            else
                m_ShieldTimer += 10.0f; // TODO: tuning
        }

        public void OnBallHit(Ball ball, Vector2 position)
        {
            if (m_IsShieldEnabled && m_ShieldHP > 0)
            {
                if (position.X < m_BoundingBox.Left || position.X > m_BoundingBox.Right)
                {
                    m_ShieldHP--;
                    if (m_ShieldHP == 0)
                        m_IsShieldEnabled = false;
                }
            }

            GameplayScreen.Instance.RequestVibration();
        }

        #endregion

        #region Helper methods

        private void ScaleSet(float newScale)
        {
            m_Scale = MathHelper.Clamp(newScale, 0.2f, 3.0f);        // TODO: TUNING: paddle size limits.
            ComputeWorldMatrix();
            m_BoundingBox.Width = (int)(Size.X * m_Scale);
        }

        private void ComputeWorldMatrix()
        {
            // TODO: opti: avoid creating temp matrices.
            m_World = Matrix.CreateRotationZ(MathHelper.PiOver2) * Matrix.CreateScale(m_Scale, 1.0f, 1.0f) * Matrix.CreateTranslation(new Vector3(Position, 0.0f));
        }

        #endregion

        #region Render
        public override void Draw(float timeFactor)
        {
            SceneInfo info = GameplayScreen.Instance.SceneInfo;

            if (m_IsShieldEnabled)
            {
                Matrix world = Matrix.CreateRotationZ(MathHelper.PiOver2) * Matrix.CreateScale(m_Scale * EXTENDED_RATIO, 1.0f, 1.0f) * Matrix.CreateTranslation(new Vector3(Position, 100.0f));
                m_Primitive.Draw(world, info.ViewMatrix, info.ProjectionMatrix, new Color(Color, 150));
            }
            m_Primitive.Draw(m_World, info.ViewMatrix, info.ProjectionMatrix, Color);

            foreach (Bullet bullet in m_Bullets)
                bullet.Draw(timeFactor);
        }

        public void DrawSmall(Vector3 position, float scale, Color color, bool isHorizontal)
        {
            SceneInfo info = GameplayScreen.Instance.SceneInfo;
            Matrix world;
            if (isHorizontal)
                world = Matrix.CreateRotationZ(MathHelper.PiOver2);
            else
                world = Matrix.Identity;
            world = world * Matrix.CreateScale(scale) * Matrix.CreateTranslation(position);
            m_Primitive.Draw(world, info.ViewMatrix, info.ProjectionMatrix, color);
        }

        #endregion
    }
}
